﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sudoku
{
    class Sudoku {

        //Constantes de classe pour la taille du sudoku
        private static int TAILLE_LIGNE = 9;
        private static int TAILLE_COLONNE = 9;


        //Attributs de la classe
        private int[,] grilleSudoku;
        private int tailleLigne; // <==== PK en plus des constantes ?
        private int tailleColonne;

        //Constructeur personnalisé
        public Sudoku()
        {
            //Initialisation de la taille à partir des constantes
            this.tailleLigne = TAILLE_LIGNE;
            this.tailleColonne = TAILLE_COLONNE;

            //Initialisation de la grille
            this.grilleSudoku = new int[this.tailleColonne, this.tailleLigne];
            this.grilleSudoku[0, 1] = 9;

        }

        // Vérification de la présence du nombre dans la ligne
        public Boolean isInRow(int valeur, int lig)
        {
            //Recherche de la valeur dans la ligne passée en paramètres
            for (int i = 0; i < TAILLE_LIGNE; i++)
            {
                if (this.grilleSudoku[i, lig] != 0 && this.grilleSudoku[i, lig] == valeur) return true;
            }

            return false;
        }

        // Vérification de la présence du nombre dans la colonne
        public Boolean isInColumn(int valeur, int col)
        {
            //Recherche de la valeur dans la colonne passée en paramètres
            for (int i = 0; i < TAILLE_COLONNE; i++)
            {
                if (this.grilleSudoku[col, i] != 0 && this.grilleSudoku[col, i] == valeur) return true;
            }

            return false;
        }

        // Vérification de la présence du nombre dans le carré
        public Boolean isInSquare(int valeur, int lig, int col)
        {
            Boolean presence = false;
            int divColonne;
            int divLigne;
            int sqrtSizeValue = (int)(Math.Sqrt(TAILLE_COLONNE));

            divColonne = col / sqrtSizeValue;
            divLigne = lig / sqrtSizeValue;

            for (int i = divColonne * sqrtSizeValue; i < divColonne * sqrtSizeValue + sqrtSizeValue; i++)
            {
                for (int j = divLigne * sqrtSizeValue; j < divLigne * sqrtSizeValue + sqrtSizeValue; j++)
                {
                    if (this.grilleSudoku[i, j] != 0 && this.grilleSudoku[i, j] == valeur) presence = true;
                }
            }

            return presence;
        }

        //Retourne les valeurs possibles à une possition
        private bool[] getValeursPossibles(int lig, int col)
        {
            bool[] result = new bool[TAILLE_LIGNE];
            for (int i = 0; i < result.Length; i++)
            {
                if (isInColumn(i, col) || isInRow(i, lig) || isInSquare(i, lig, col))
                    result[i] = false;
                else
                    result[i] = true;
            }
            return result;
        }

        //Generation des nombre du sudoku
        public void generer()
        {
            this.grilleSudoku = new int[this.tailleColonne, this.tailleLigne];

            //Réference aux bornes pour la génération aléatoire
            int min = 1, max = 10;
            Random rnd = new Random();

            //Neud pour le backtracking => sera le neud actuel
            Node backtrack = new Node();

            /*delegate void tryNext(Node backtrack, int val);
            tryNext tryNext = delegate(Node backtrack, int val) {
                backtrack.lChild[val] = new Node();
                backtrack = backtrack.lChild[val];
                backtrack.value = val;
            };***/

            //gestion de la remonté lorsque toutes les valeurs ont été testées
            bool goBack = false;

            //Parcours
            for (int i = 0; i < TAILLE_COLONNE; i++)
            {
                //Console.WriteLine("Colonne : " + i);
                for (int j = 0; j < TAILLE_LIGNE; j++)
                {
                    //Console.WriteLine("Ligne : " + j);
                    //On cherche une valeur possible
                    while(true) {

                        //Si on a tout essayé, on remonte le backtrack
                        if (backtrack.isChildsFull()) 
                        {
                            goBack = true;
                            break;
                        }

                        // Test d'une valeur aléatoire
                        int val = rnd.Next(min, max);
                        if (backtrack.lChild[val - 1] != null)
                            continue; // valeur déjà essayée
                        else
                            backtrack.lChild[val - 1] = new Node(backtrack); // enregistrement de l'essay de variable

                        // Verification de la valeur aléatoire
                        if (!(isInColumn(val, i) || isInRow(val, j) || isInSquare(val, j, i)))
                        {
                            backtrack = backtrack.lChild[val-1];
                            backtrack.value = val;
                            break;
                        } 
                    }

                    //Remonté de backtrack
                    if (goBack)
                    {
                        grilleSudoku[i, j] = 0;
                        backtrack = backtrack.nParent;

                        //Remonté d'une colonne
                        if (j == 0) {
                            j = TAILLE_COLONNE - 2;
                            i--;
                            //Console.WriteLine("Colonne : " + i);
                        } else //remonté d'une ligne
                            j -= 2;

                        goBack = false;
                        continue;
                    }

                    //Ajout au Sudoku
                    grilleSudoku[i, j] = backtrack.value;
                }

                /*for (int j = 0; j <= i; j++){}
                for (int j = i - 1; j >= 0; j--)
                {grilleSudoku[j, i]}***/
            }
        }//generer()


        //
        // Getters / Setters
        //

        // Getter grille
        public int[,] getGrilleSudoku()
        {
            return this.grilleSudoku;
        }

        // Getter taille ligne
        public int getTailleLigne()
        {
            return this.tailleLigne;
        }

        // Getter taille colonne
        public int getTailleColonne()
        {
            return this.tailleColonne;
        }

        // Setter grille 
        public void setGrilleSudoku(int[,] grille)
        {
            this.grilleSudoku = grille;
        }

        // Setter taille ligne
        public void setTailleLigne(int tailleLigne)
        {
            this.tailleLigne = tailleLigne;
        }

        // Setter taille colonne
        public void setTailleColonne(int tailleColonne)
        {
            this.tailleColonne = tailleColonne;
        }


    }
}
